package org.jackysoft.service;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.jackysoft.entity.AbstractReporter;
import org.jackysoft.entity.AcegiRole;
import org.jackysoft.entity.ArmDailyProductReporter;
import org.jackysoft.entity.ArmItem;
import org.jackysoft.entity.DataItem;
import org.jackysoft.entity.Department;
import org.jackysoft.entity.ProductType;
import org.jackysoft.entity.User;
import org.jackysoft.report.ArmItemEqualPredicate;
import org.jackysoft.report.DataItemUtil;
import org.jackysoft.report.ReportConstant;
import org.jackysoft.report.ReporterUtil;
import org.jackysoft.report.visitor.ArmItemVisitor;
import org.jackysoft.util.CollectionUtils;
import org.jackysoft.util.SpringSecurityUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * 报表 services 实现类,实现了基本业务操作
 */

@Service
@Transactional
public abstract class AbstractReportServiceImpl<T extends AbstractReporter>
		extends AbstractStringIdServiceImpl<T> implements ReporterService<T> {

	private static final long serialVersionUID = 8290362901032898939L;
	private static final Log logger = LogFactory
			.getLog(AbstractReportServiceImpl.class);
	protected ArmItemEqualPredicate armItemEqualPredicate;
	protected ArmItemVisitor accumulateVisitor;
	protected ProductService productService;
    private ReportResetedService retService;
	@Value("${report.manager}")
	protected String reportManagers;	

	protected DataItemUtil dataItemUtil;
	protected ReporterUtil reportUtil;

	public AbstractReportServiceImpl() {

	}

	public AbstractReportServiceImpl(Class<T> type) {
		super(type);
	}
	
	protected boolean checkAuthority() {
		User user = SpringSecurityUtils.getCurrentUser();
		List<String> managers = Arrays.asList(reportManagers.split("\\,"));
		Collection<AcegiRole> auths = user.getAuthoritys();
		if (auths == null || auths.size() == 0)
			return false;
		for (String mrg : managers) {
			AcegiRole nar = new AcegiRole(mrg, null);
			if (auths.contains(nar))
				return true;
		}
		return false;
	}

	@Override
	public void setReportType(Class<T> rtype) {
		this.setType(rtype);

	}

	@Override
	public boolean resetable(DateTime today) {		
	
		int todayDay = today.getDayOfMonth();
			
		return todayDay==27;
	}

	/**
	 * 获取指定年份指定部门最后一张报表
	 * 
	 * @param year
	 *            年份
	 * @param department
	 *            部门
	 * @return 该部门该年,最后一张报表
	 * */
	protected T lastOfYear(int year, Department department) {

		String hql = String
				.format("from "
						+ " %s r where r.department=:outdept and r.createDate=(select max(t.createDate) from "
						+ " %s t where year(t.createDate)=:year and t.department=:indept)",
						table(), table());

		Query query = query(hql)
				.setParameter("year", year)
				.setParameter("indept", department)
				.setParameter("outdept", department);
		Iterator itr = query.iterate();
		return (T) (itr.hasNext() ? itr.next() : null);
	}

	

	/**
	 * 获取本部门最后一张报表
	 * */
	@SuppressWarnings("rawtypes")
	public String getLast(Department department) throws DataAccessException {

		String hql = String
				.format("select r.id from %s r where r.department.id='%s' and  r.createDate=(select max(t.createDate) from %s t where t.department.id='%s')",
						table(), department.getId(), table(),
						department.getId());

		Iterator itr = query(hql).iterate();
		return (String) (itr.hasNext() ? itr.next() : null);
	}

	/**
	 * 更新累计
	 * 
	 * @param t
	 *            当前报表
	 * @param last
	 *            上次报表
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * 
	 * */
	protected void updateAccumulate(T t, T last) {
		if (last == null) {
			last = emptyReport();
		}
		Collection<ArmItem> items = dataItemUtil.mergeArmItems(t.getArmItems());
		Collection<ArmItem> itemssrc = dataItemUtil.mergeArmItems(last.getArmItems());

		for (ArmItem itm : items) {
			if (itm == null)
				continue;
			armItemEqualPredicate.setHost(itm.getHost());
			ArmItem src = (ArmItem) CollectionUtils.find(itemssrc,
					armItemEqualPredicate);
			accumulateVisitor.setSource(src);
			itm.accetp(accumulateVisitor);
		}
		List<ArmItem> litems = Lists.newArrayList();
		litems.addAll(items);
		t.getArmItems().clear();
		t.getArmItems().addAll(litems);
	}

	@SuppressWarnings("unchecked")
	protected T emptyReport()  {
		User cu = SpringSecurityUtils.getCurrentUser();
		T t = null;
		try {
			t = (T) getType().newInstance();
		} catch (InstantiationException e) {
			//logger.error(e);
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			//logger.error(e);
			e.printStackTrace();
		}
		t.setMaker(cu);
		t.setDepartment(cu.getDepartment());
		t.setCreateDate(DateTime.now());
		return t;
	}

	@Override
	public void create(T b) throws DataAccessException {

		DateTime cal = getReportCreateDate(b);
		b.setCreateDate(cal);
		super.create(b);
	}

	@Override
	public void update(T b) throws DataAccessException {

		DateTime cal = getReportCreateDate(b);
		b.setCreateDate(cal);
		super.update(b);
	}

	@Override
	public void remove(T t) throws DataAccessException {

		super.remove(t);
	}

	@Override
	public void saveUpdate(T t) throws DataAccessException {

		super.saveOrUpdate(t);		

	}

	DateTime getReportCreateDate(T b) {
		DateTime cal = b.getCreateDate();
		cal = cal==null?DateTime.now():cal;
    	DateTime date = new DateTime(
    			cal.getYear()
    			,cal.getMonthOfYear()
    			,cal.getDayOfMonth()
    			,cal.getHourOfDay()
    			,cal.getMinuteOfHour()
    			,cal.getSecondOfMinute()
    			);
		return date;
	}

	/**
	 * 设置报表结束状态
	 * 
	 * */
	@Override
	public void submit(T rpt,boolean finished) throws DataAccessException {
		rpt = get(rpt.getId());
		rpt.setFinished(finished);
	}
	
	
	
	@Override
	public org.w3c.dom.Document w3cDocmentDataSource(String id)
			throws DataAccessException {
		throw new UnsupportedOperationException("请实现该方法");
	}

	@Override
	public T get(DateTime tdate, Department department)
			throws DataAccessException {
		
		String hql = String
				.format("from %s r where r.department.id='%s' and datediff(date(r.createDate),date('%s'))=0",
						table(), department.getId(),tdate.toString("yyyy-MM-dd"));
		Query q = query(hql);
		Iterator itr = q.iterate();
		return (T) (itr.hasNext() ? itr.next() : null);
	}

	@Override
	public Collection<T> gets(DateTime from, DateTime to, Department department)
			throws DataAccessException {
		String hql = String
				.format("from  %s r where r.department=:dept1 and r.createDate = (select max(b.createDate) from %s b where b.department=:dept2 "
						+ "and b.createDate<:toDate and b.createDate>:fromDate ) ",
						table(), table());
		Session sess = getSession();
		List itr = sess.createQuery(hql).setParameter("dept1", department)
				.setParameter("dept2", department)
				.setParameter("fromDate", from).setParameter("toDate", to)
				.list();
		return itr;
	}

	@Override
	@Transactional(isolation = Isolation.READ_UNCOMMITTED, readOnly = true)
	public T prepareSelect(DateTime cal) throws DataAccessException,
			InstantiationException, IllegalAccessException {
		User cu = SpringSecurityUtils.getCurrentUser();
		Department dept=  cu.getDepartment();
		boolean resetable = resetable(cal);
		boolean rptreset  = retService.checkResetable(dept, cal);
		if (resetable || rptreset)
			return emptyReport();
		
		
		String tid = getLast(dept);
		T t = tid != null ? get(tid) : null;
		t = (T) (t == null ? emptyReport() : t.clone());
		t.setId("");
		t.setCreateDate(DateTime.now());
		resetReportData(t);
		classifiedReport(t);
		return t;
	}

	/**
	 * 重置报表数据项为零
	 * 
	 * 
	 * */
	public void resetReportData(T t) {
		for (ArmItem item : t.getArmItems()) {
			for (Map.Entry<String, DataItem> dataEntry : item.getDataItems()
					.entrySet()) {
				dataEntry.getValue().setAtomValue(0D);
				// dataEntry.getValue().setAccumulateValue(0D);
			}
		}
	}

	@Resource
	public void setProductService(ProductService productService) {
		this.productService = productService;
	}

	@Override
	public T get(String id) throws DataAccessException {
		T report = super.get(id);
		classifiedReport(report);
		return report;
	}

	/**
	 * 报表产品归类化
	 * */
	protected void classifiedReport(T report) {
		if (report == null)
			return;
		List<ArmItem> children = report.getArmItems();
		
		Map<String, List<ArmItem>> clsItems = classifiedArmItems(children);
		report.setClassifiedItems(clsItems);

	}

	/**
	 * 产品数据分类化
	 * */
	protected Map<String, List<ArmItem>> classifiedArmItems(
			Collection<ArmItem> children) {
		if (children == null)
			return Collections.emptyMap();
		Map<String, List<ArmItem>> clsItems = Maps.newHashMap();
		for (ArmItem item : children) {			
			ProductType pt = item.getItemType();
			if (pt == null)
				pt = ProductType.常规产品;
			List<ArmItem> titems = clsItems.get(pt.toString());
			if (titems == null) {
				titems = Lists.newArrayList();
				clsItems.put(pt.toString(), titems);
			}
			titems.add(item.clone());
		}
		for (Map.Entry<String, List<ArmItem>> entry : clsItems.entrySet()) {
			List<ArmItem> ais = entry.getValue();
			Collections.sort(ais);
		}
		return clsItems;
	}

	@Resource
	public void setDataItemUtil(DataItemUtil dataItemUtil) {
		this.dataItemUtil = dataItemUtil;
	}

	@Resource
	public void setReportUtil(ReporterUtil reportUtil) {
		this.reportUtil = reportUtil;
	}
	
	@Resource(name = ReportConstant.ArmItemEqualPredicate)
	public void setArmItemEqualPredicate(
			ArmItemEqualPredicate armItemEqualPredicate) {
		this.armItemEqualPredicate = armItemEqualPredicate;
	}

	public ArmItemEqualPredicate getArmItemEqualPredicate() {
		return armItemEqualPredicate;
	}

	public void setAccumulateVisitor(ArmItemVisitor accumulateVisitor) {
		this.accumulateVisitor = accumulateVisitor;
	}

	public ArmItemVisitor getAccumulateVisitor() {
		return accumulateVisitor;
	}

	@Resource
	public void setRetService(ReportResetedService retService) {
		this.retService = retService;
	}	

}
